package com.xiyue.leaspring.config;

import com.xiyue.leaspring.filter.ValidatorCodeUsernamePasswordAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleHierarchyVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

/**
 * 描述
 *
 * @author xiyue
 * @version 1.0
 * @date 2021/04/19 23:48:24
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)//启用注解配置
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;//数据源
    @Autowired
    private UserDetailsService userDetailsService;//用户服务
    @Autowired
    private SavedRequestAwareAuthenticationSuccessHandler successHandler;//成功页
    @Autowired
    private SimpleUrlAuthenticationFailureHandler failureHandler;//失败页
    @Autowired
    private SessionInformationExpiredStrategy sessionExpiredStrategy;//Session失效策略
    @Autowired
    private UsernamePasswordAuthenticationFilter authenticationFilter;//认证过滤器
    @Autowired
    private JdbcTokenRepositoryImpl tokenRepository;//token存储
    @Autowired
    private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private RoleHierarchy roleHierarchy;//角色继承

    @Autowired
    private SecurityExpressionHandler<FilterInvocation> expressionHandler;//表达式处理器

    @Autowired
    private AccessDecisionManager accessDecisionManager;//投票管理器

    @Bean
    public AccessDecisionManager getAccessDecisionManager() {
        //将所有用到的投票器设置到List集合中
        List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
        decisionVoters.add(new RoleVoter());//角色投票器
        decisionVoters.add(new AuthenticatedVoter());//认证投票器
        decisionVoters.add(new RoleHierarchyVoter(this.roleHierarchy));//角色继承投票器
        WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
        webExpressionVoter.setExpressionHandler(this.expressionHandler);
        decisionVoters.add(webExpressionVoter);//表达式解析
        AffirmativeBased accessDecisionManager = new AffirmativeBased(decisionVoters);//定义投票管理器
        return accessDecisionManager;
    }

    @Bean
    public SecurityExpressionHandler<FilterInvocation> getExpressionHandler() {//配置表达式
        DefaultWebSecurityExpressionHandler expressionHandler = new DefaultWebSecurityExpressionHandler();
        expressionHandler.setRoleHierarchy(this.roleHierarchy);//设置角色继承
        return expressionHandler;
    }

    @Bean
    public RoleHierarchy getRoleHierarchy() {//角色继承设置
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();//角色继承
        roleHierarchy.setHierarchy("ROLE_ADMIN>ROLE_USER");//继承关系
        return roleHierarchy;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //进行用户角色配置时不需要追加ROLE_前缀,系统会自动添加
//        auth.inMemoryAuthentication()//固定认证信息
//                .withUser("admin")//用户名
//                .password("{bcrypt}$2a$10$L1/V.lk9yj2wVSfbGpxbQO8WFrqot5Xck9Yd/I5Tzy/vgCYKjEv16")//密码
//                .roles("USER","ADMIN");//角色
//        auth.inMemoryAuthentication()//固定认证信息
//                .withUser("xiyue")//用户名
//                .password("{bcrypt}$2a$10$TC/ROdGr5fjmmm/OmYu13ui6LmTJWbdSTid/9yWYp9SaolZa095Em")//密码
//                .roles("USER");//角色
        auth.userDetailsService(this.userDetailsService);//基于数据库认证
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        http.csrf().disable();//禁用CSRF验证
//        //配置拦截路径的匹配地址与限定授权访问
//        http.authorizeRequests()//配置认证请求
//            .antMatchers("/pages/message/**").access("hasRole('ADMIN')")//授权访问
//            .antMatchers("/welcomePage").authenticated()//认证访问
//            .antMatchers("/**").permitAll();//任意访问
//        //配置HTTP登录,注销与错误路径
//        http.formLogin()//登录配置
//            .usernameParameter("mid")//用户名参数设置
//            .passwordParameter("pwd")//用户密码参数设置
//            .successForwardUrl("/welcomePage")//登录成功路径
//            .loginPage("/loginPage")//登录表单页面
//            .loginProcessingUrl("/xylogin")//登录路径
//            .failureForwardUrl("/loginPage?error=true")//登录失败路径
//            .and()//配置连接
//            .logout()//注销配置
//            .logoutUrl("/xylogout")//注销路径
//            .logoutSuccessUrl("/logoutPage")//注销成功路径
//            .deleteCookies("JSESSIONID")//删除Cookie
//            .and()//配置连接
//            .exceptionHandling()//认证错误配置
//            .accessDeniedPage("/WEB-INF/pages/error_page_403.jsp");//授权错误
        http.csrf().disable();//禁用CSRF验证
        http.httpBasic().authenticationEntryPoint(this.authenticationEntryPoint);
        //进行拦截路径的匹配地址配置与授权访问限定
        http.authorizeRequests()//配置认证请求
            .antMatchers("/pages/message/**").access("hasRole('ADMIN')")//授权访问
            .antMatchers("/welcomePage").authenticated();//认证访问
        //进行http注销与错误路径配置,登录操作将由过滤器负责完成
        http.logout()//注销配置
            .logoutUrl("/xylogout")//注销路径
            .logoutSuccessUrl("/logoutPage")//注销成功路径
            .deleteCookies("JSESSIONID")//删除Cookie
            .and()//配置连接
            .exceptionHandling()//认证错误配置
            .accessDeniedPage("/WEB-INF/pages/error_page_403.jsp");//授权错误页
        http.rememberMe()//开启RememberMe
            .rememberMeParameter("remember")//表单参数
            .key("xy-xiyue")//加密key
            .tokenValiditySeconds(2592000)//失效时间
            .rememberMeCookieName("xiyue-rememberme-cookie")//Cookie名称
            .tokenRepository(this.tokenRepository);//持久化
        http.sessionManagement()//Session管理
            .invalidSessionUrl("/loginPage")//失效路径
            .maximumSessions(1)//并发Session
            .expiredSessionStrategy(this.sessionExpiredStrategy);//失效策略
        http.addFilterBefore(this.authenticationFilter,UsernamePasswordAuthenticationFilter.class);//追加过滤器
    }
    @Override
    public void configure(WebSecurity web) throws Exception{
        web.ignoring().antMatchers("/index.jsp");//忽略的验证路径
    }

    @Bean
    public UsernamePasswordAuthenticationFilter getAuthenticationFilter() throws Exception{
        ValidatorCodeUsernamePasswordAuthenticationFilter filter = new
                ValidatorCodeUsernamePasswordAuthenticationFilter();
        filter.setAuthenticationManager(super.authenticationManager());//认证管理器
        filter.setAuthenticationSuccessHandler(this.successHandler);//登录成功页面
        filter.setAuthenticationFailureHandler(this.failureHandler);//登录失败页面
        filter.setFilterProcessesUrl("/xylogin");//登录路径
        filter.setUsernameParameter("mid");//参数名称
        filter.setPasswordParameter("pwd");//参数名称
        return filter;
    }

    @Bean
    public LoginUrlAuthenticationEntryPoint getAuthenticationEntryPoint() {
        return new LoginUrlAuthenticationEntryPoint("/loginPage");
    }

    @Bean
    public SavedRequestAwareAuthenticationSuccessHandler getSuccessHandler() {//认证成功
        SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setDefaultTargetUrl("/welcomePage");//成功页面
        return successHandler;
    }

    @Bean
    public SimpleUrlAuthenticationFailureHandler getFailureHandler() {//认证失败处理
        SimpleUrlAuthenticationFailureHandler handler = new SimpleUrlAuthenticationFailureHandler();
        handler.setDefaultFailureUrl("/loginPage?error=true");//失败页面
        return handler;
    }

    @Bean
    public JdbcTokenRepositoryImpl getTokenRepository() {//持久化Cookie
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(this.dataSource);
        return tokenRepository;
    }

    @Bean
    public SessionInformationExpiredStrategy getSessionExpiredStrategy() {
        //Session失效策略,同时配置失效后的跳转路径
        return new SimpleRedirectSessionInformationExpiredStrategy("/logoffPage");
    }
}
